﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Drawing;
using System.Runtime.Serialization;
using System.Drawing.Imaging;
using System.Diagnostics.CodeAnalysis;
using System.ComponentModel;
using System.Collections.Specialized;
using System.ComponentModel.DataAnnotations;

namespace DarkLight.Model
{
    [DataContract]
    public class Moon : Orthonormality, IEnvironment
    {
        #region Constructors
        public Moon(float diameter)
        {
            Radius = diameter / 2;
        }
        #endregion

        #region Public Members
        [DataMember]
        [Required(ErrorMessage = "Name required")]
        public string Name
        {
            get { return _name; }
            set
            {
                if (_name != value)
                {
                    RaisePropertyChanging("Name");
                    _name = value;
                    RaisePropertyChanged("Name");
                }
            }
        }
        private string _name;

        [DataMember]
        public float Radius
        {
            get { return _radius; }
            set
            {
                if (_radius != value)
                {
                    RaisePropertyChanging("Radius");
                    _radius = value;
                    RaisePropertyChanged("Radius");
                }
            }
        }
        private float _radius;

        public SpaceRegion NearSpace
        {
            get { return _nearSpace; }
            set
            {
                if (!object.ReferenceEquals(_nearSpace, value))
                {
                    RaisePropertyChanging("NearSpace");
                    _nearSpace = value;
                    RaisePropertyChanged("NearSpace");
                }
            }
        }
        private SpaceRegion _nearSpace;

        public SpaceRegion FarSpace
        {
            get { return _farSpace; }
            set
            {
                if (!object.ReferenceEquals(_farSpace, value))
                {
                    RaisePropertyChanging("FarSpace");
                    _farSpace = value;
                    RaisePropertyChanged("FarSpace");
                }
            }
        }
        private SpaceRegion _farSpace;

        [DataMember]
        public float DiffuseCoefficient
        {
            get { return _DiffuseCoefficient; }
            set 
            {
                if (_DiffuseCoefficient != value)
                {
                    RaisePropertyChanging("DiffuseCoefficient");
                    _DiffuseCoefficient = value;
                    RaisePropertyChanged("DiffuseCoefficient");
                }
            }
        }
        private float _DiffuseCoefficient;

        public Bitmap DiffuseTexture
        {
            get { return _DiffuseTexture; }
            set 
            {
                if (_DiffuseTexture != value)
                {
                    RaisePropertyChanging("DiffuseTexture");
                    _DiffuseTexture = value;
                    RaisePropertyChanged("DiffuseTexture");
                }
            }
        }
        private Bitmap _DiffuseTexture;

        public Bitmap GlowTexture
        {
            get { return _GlowTexture; }
            set 
            {
                if (_GlowTexture != value)
                {
                    RaisePropertyChanging("GlowTexture");
                    _GlowTexture = value;
                    RaisePropertyChanged("GlowTexture");
                }
            }
        }
        private Bitmap _GlowTexture;

        public Bitmap BumpTexture
        {
            get { return _BumpTexture; }
            set 
            {
                if (_BumpTexture != value)
                {
                    RaisePropertyChanging("BumpTexture");
                    _BumpTexture = value;
                    RaisePropertyChanged("BumpTexture");
                }
            }
        }
        private Bitmap _BumpTexture;

        public Bitmap SpriteTexture
        {
            get { return _SpriteTexture; }
            set 
            {
                if (_SpriteTexture != value)
                {
                    RaisePropertyChanging("SpriteTexture");
                    _SpriteTexture = value;
                    RaisePropertyChanged("SpriteTexture");
                }
            }
        }
        private Bitmap _SpriteTexture;
        #endregion

        #region ObservableObject Overrides
        protected override void OnPropertyChanging(System.ComponentModel.PropertyChangingEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");

            base.OnPropertyChanging(e);
            if (e.PropertyName == "NearSpace" && !object.ReferenceEquals(this.NearSpace, null))
            {
                FarSpace.ParentOrthonormality = null;
                if (!object.ReferenceEquals(this.NearSpace.Fleets, null))
                    this.NearSpace.Fleets.CollectionChanged -= NearSpace_Fleets_CollectionChanged;
                NearSpace.PropertyChanging -= NearSpace_PropertyChanging;
                NearSpace.PropertyChanged -= NearSpace_PropertyChanged;
            }
            else if (e.PropertyName == "FarSpace" && !object.ReferenceEquals(this.FarSpace, null))
            {
                FarSpace.ParentOrthonormality = null;
                if (!object.ReferenceEquals(this.FarSpace.Fleets, null))
                    this.FarSpace.Fleets.CollectionChanged -= FarSpace_Fleets_CollectionChanged;
                FarSpace.PropertyChanging -= FarSpace_PropertyChanging;
                FarSpace.PropertyChanged -= FarSpace_PropertyChanged;
            }
        }

        protected override void OnPropertyChanged(System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");

            base.OnPropertyChanged(e);

            if (e.PropertyName == "NearSpace" && !object.ReferenceEquals(this.NearSpace, null))
            {
                NearSpace.ParentOrthonormality = this;
                NearSpace.PropertyChanging += NearSpace_PropertyChanging;
                NearSpace.PropertyChanged += NearSpace_PropertyChanged;
                if (!object.ReferenceEquals(this.NearSpace.Fleets, null))
                    this.NearSpace.Fleets.CollectionChanged += NearSpace_Fleets_CollectionChanged;
            }
            else if (e.PropertyName == "FarSpace" && !object.ReferenceEquals(this.FarSpace, null))
            {
                FarSpace.ParentOrthonormality = this;
                FarSpace.PropertyChanging += FarSpace_PropertyChanging;
                FarSpace.PropertyChanged += FarSpace_PropertyChanged;
                if (!object.ReferenceEquals(this.FarSpace.Fleets, null))
                    this.FarSpace.Fleets.CollectionChanged += FarSpace_Fleets_CollectionChanged;
            }

            //if (e.PropertyName == "DiffuseCoefficient")
            //{
            //    this._effect.GetVariableByName("g_fDiffuseCoefficient").AsScalar().Set(DiffuseCoefficient);
            //}
            //else if (e.PropertyName == "DiffuseTexture")
            //{
            //    TextureHelper.SetTexture(this._effect, "diffuse_texture", DiffuseTexture);
            //}
            //else if (e.PropertyName == "GlowTexture")
            //{
            //    TextureHelper.SetTexture(this._effect, "glow_texture", GlowTexture);
            //}
            //else if (e.PropertyName == "BumpTexture")
            //{
            //    TextureHelper.SetTexture(this._effect, "bump_texture", BumpTexture);
            //}
            //else if (e.PropertyName == "SpriteTexture")
            //{
            //    TextureHelper.SetTexture(this._effect, "sprite_texture", SpriteTexture);
            //}
        }
        #endregion

        #region Event Handlers
        private void NearSpace_PropertyChanging(object sender, PropertyChangingEventArgs e)
        {
            if (e.PropertyName == "Fleets" && !object.ReferenceEquals(NearSpace.Fleets, null))
                NearSpace.Fleets.CollectionChanged -= NearSpace_Fleets_CollectionChanged;
        }

        private void NearSpace_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Fleets" && !object.ReferenceEquals(NearSpace.Fleets, null))
                NearSpace.Fleets.CollectionChanged += NearSpace_Fleets_CollectionChanged;
        }

        private void NearSpace_Fleets_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                var rnd = new Random(NearSpace.Fleets.Count);
                foreach (Fleet fleet in e.NewItems)
                {
                    var theta = (float)(rnd.NextDouble() * 2 * Math.PI);
                    var phi = (float)(rnd.NextDouble() * 2 * Math.PI);
                    fleet.Position = new Spherical(this.Radius * 5.0f, theta, phi);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (Fleet fleet in e.NewItems)
                {
                    fleet.Position = Spherical.Zero;
                }
            }
        }

        private void FarSpace_PropertyChanging(object sender, PropertyChangingEventArgs e)
        {
            if (e.PropertyName == "Fleets" && !object.ReferenceEquals(FarSpace.Fleets, null))
                FarSpace.Fleets.CollectionChanged -= FarSpace_Fleets_CollectionChanged;
        }

        private void FarSpace_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Fleets" && !object.ReferenceEquals(FarSpace.Fleets, null))
                FarSpace.Fleets.CollectionChanged += FarSpace_Fleets_CollectionChanged;
        }

        private void FarSpace_Fleets_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                var rnd = new Random(FarSpace.Fleets.Count);
                foreach (Fleet fleet in e.NewItems)
                {
                    var theta = (float)(rnd.NextDouble() * 2 * Math.PI);
                    var phi = (float)(rnd.NextDouble() * 2 * Math.PI);
                    fleet.Position = new Spherical(this.Radius * 20.0f, theta, phi);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (Fleet fleet in e.NewItems)
                {
                    fleet.Position = Spherical.Zero;
                }
            }
        }
        #endregion

        #region IOrthonormality Implementation
        protected internal override IEnumerable<IPosition> ChildElements
        {
            get
            {
                var childs = new List<IPosition>();
                childs.Add(NearSpace);
                childs.Add(FarSpace);
                return childs;
            }
        }
        #endregion

        #region Serialization Members
        [DataMember]
        [SuppressMessage("Microsoft.Performance", "CA1811")]
        private byte[] DiffuseTextureByteArray
        {
            get
            {
                if (this.DiffuseTexture == null)
                    return null;
                using (var ms = new MemoryStream())
                {
                    this.DiffuseTexture.Save(ms, ImageFormat.Png);
                    return ms.ToArray();
                }
            }
            set
            {
                if (value != null)
                {
                    using (var ms = new MemoryStream())
                    {
                        ms.Write(value, 0, value.Count());
                        this.DiffuseTexture = new Bitmap(Image.FromStream(ms));
                    }
                }
                else
                    this.DiffuseTexture = null;
            }
        }

        [DataMember]
        [SuppressMessage("Microsoft.Performance", "CA1811")]
        private byte[] GlowTextureByteArray
        {
            get
            {
                if (this.GlowTexture == null)
                    return null;
                using (var ms = new MemoryStream())
                {
                    this.GlowTexture.Save(ms, ImageFormat.Png);
                    return ms.ToArray();
                }
            }
            set
            {
                if (value != null)
                {
                    using (var ms = new MemoryStream())
                    {
                        ms.Write(value, 0, value.Count());
                        this.GlowTexture = new Bitmap(Image.FromStream(ms));
                    }
                }
                else
                    this.GlowTexture = null;
            }
        }

        [DataMember]
        [SuppressMessage("Microsoft.Performance", "CA1811")]
        private byte[] BumpTextureByteArray
        {
            get
            {
                if (this.BumpTexture == null)
                    return null;
                using (var ms = new MemoryStream())
                {
                    this.BumpTexture.Save(ms, ImageFormat.Png);
                    return ms.ToArray();
                }
            }
            set
            {
                if (value != null)
                {
                    using (var ms = new MemoryStream())
                    {
                        ms.Write(value, 0, value.Count());
                        this.BumpTexture = new Bitmap(Image.FromStream(ms));
                    }
                }
                else
                    this.BumpTexture = null;
            }
        }

        [DataMember]
        [SuppressMessage("Microsoft.Performance", "CA1811")]
        private byte[] SpriteTextureByteArray
        {
            get
            {
                if (this.SpriteTexture == null)
                    return null;
                using (var ms = new MemoryStream())
                {
                    this.SpriteTexture.Save(ms, ImageFormat.Png);
                    return ms.ToArray();
                }
            }
            set
            {
                if (value != null)
                {
                    using (var ms = new MemoryStream())
                    {
                        ms.Write(value, 0, value.Count());
                        this.SpriteTexture = new Bitmap(Image.FromStream(ms));
                    }
                }
                else
                    this.SpriteTexture = null;
            }
        }
        #endregion

        #region Rendering
        //private void InitializeComponent()
        //{
        //    _effect = this.CreateEffect(Properties.Resources.Moon);
        //    _renderLayout = this.CreateRenderLayout(_effect);
        //    _spriteLayout = this.CreateSpriteLayout(_effect);
        //    _trajLayout = this.CreateTrajectoryLayout(_effect);

        //    if (object.ReferenceEquals(this.Position, null))
        //        this.Position = new SphericalCS(0, 0, (float)Math.PI / 2);
        //    if (object.ReferenceEquals(this.Orientation, null))
        //        this.Orientation = RotationCS.Zero;
        //    if (object.ReferenceEquals(this.NearSpace, null))
        //        this.NearSpace = new SpaceRegion();
        //    if (object.ReferenceEquals(this.FarSpace, null))
        //        this.FarSpace = new SpaceRegion();

        //    this.DiffuseCoefficient = 1.0f;
        //    this._effect.GetVariableByName("g_spriteDistance").AsScalar().Set(EngineParameters.SPRITE_DISTANCE);
        //    using (var bumpSRV = new ShaderResourceView(Engine.Current.Device, Properties.Resources.Neutral.ToTexture2D()))
        //        this._effect.GetVariableByName("bump_texture").AsResource().SetResource(bumpSRV);
        //}

        //protected override bool CanRender<T>(T objectToRender, float distanceToCamera)
        //{
        //    if (typeof(T) == typeof(IRender3D))
        //    {
        //        return distanceToCamera
        //            < this.Radius
        //            * EngineParameters.SYSTEM_MAX_RADIUS_MOON;
        //        /// EngineParameters.CAMERA_REDUC_COEFF
        //        //* EngineParameters.SPACE_ELEMENT_ZOOM_COEFF;
        //    }
        //    else if (typeof(T) == typeof(IRenderSprite))
        //    {
        //        return distanceToCamera
        //            < this.Position.Rho
        //            * EngineParameters.SYSTEM_MAX_RADIUS_MOONS;
        //        /// EngineParameters.CAMERA_REDUC_COEFF
        //        //* EngineParameters.SPACE_ELEMENT_ZOOM_COEFF;
        //    }
        //    else if (typeof(T) == typeof(IRenderTrajectory))
        //    {
        //        return distanceToCamera
        //            < this.Position.Rho
        //            * EngineParameters.SYSTEM_MAX_RADIUS_MOONS;
        //        /// EngineParameters.CAMERA_REDUC_COEFF
        //        //* EngineParameters.SPACE_ELEMENT_ZOOM_COEFF;
        //    }
        //    else if (typeof(T) == typeof(IRender))
        //    {
        //        return distanceToCamera
        //            < this.Position.Rho
        //            * EngineParameters.SYSTEM_MAX_RADIUS_MOONS;
        //        /// EngineParameters.CAMERA_REDUC_COEFF
        //        //* EngineParameters.SPACE_ELEMENT_ZOOM_COEFF;
        //    }
        //    return false;
        //}
        #endregion

        public IEnumerable<ILocation> Environment
        {
            get
            {
                var childs = new List<ILocation>();
                childs.Add(NearSpace);
                childs.Add(FarSpace);
                return childs;
            }
        }
    }
}
